#pragma once

#include "ConstantTable.h"
#include "Utilities.h"

ConstantTable::ConstantTable(void){
	constantTable = ConstantTableType();
	lineTable = LineTableType();
}

ConstantTable::~ConstantTable(void){
	clear();
}

void ConstantTable::clear(){
	constantTable.clear();
	lineTable.clear();
}

// insert constant with the given line, returns status of operation
bool ConstantTable::insertConstant(Type::Index::Line line, Type::Entity::Line entityType, Type::Constant con){

	// invlaid line index or constant value or entity
	if(!Utilities::isValidLineIndex(line) || !Utilities::isValidLineEntity(entityType) 
		|| !Utilities::isValidConstant(con)){
			return false;
	}

	Type::List::Constant conList;
	Type::Map::EntityLine entityLineMap;
	Type::List::Line allLineList;
	Type::List::Line lineList;
	Type::Entity::Line allLineType = Type::Entity::ALL_LINE_ENTITY;
	bool isNotExisted = true;

	if(isLineExistsInConstant(line)){
		conList = constantTable.at(line);
		constantTable.erase(line);
	}

	if(isConstantExistsInLine(con)){
		entityLineMap = lineTable.at(con);
		lineTable.erase(con);

		// find ALL entity in EntityLineMap
		if(!entityLineMap.empty() && entityLineMap.find(allLineType) != entityLineMap.end()){
			allLineList = entityLineMap.at(allLineType);
			entityLineMap.erase(allLineType);
		}

		// find inserted entity in EntityLineMap
		if(!entityLineMap.empty() && entityLineMap.find(entityType) != entityLineMap.end()){
			lineList = entityLineMap.at(entityType);
			entityLineMap.erase(entityType);
		}
	}

	if(Utilities::isExistInList(conList, con) || Utilities::isExistInList(allLineList, line)){
		isNotExisted = false;
	}

	/******************constantTable*********************/
	if(isNotExisted){
		conList.push_back(con);
	}
	constantTable.insert(std::make_pair<Type::Index::Line, Type::List::Constant>(line, conList));

	/********************lineTable***********************/
	if(isNotExisted){
		allLineList.push_back(line);
		lineList.push_back(line);
	}
	entityLineMap.insert(std::make_pair<Type::Entity::Line, Type::List::Line>(allLineType, allLineList));	
	entityLineMap.insert(std::make_pair<Type::Entity::Line, Type::List::Line>(entityType, lineList));
	
	lineTable.insert(std::make_pair<Type::Constant, Type::Map::EntityLine>(con, entityLineMap));

	return isNotExisted;
}

// get all constants
const Type::List::Constant& ConstantTable::getConstants(){
	Type::List::Constant* list = new Type::List::Constant();

	if(lineTable.size() > 0){
		for(LineTableType::iterator it=lineTable.begin(); it!=lineTable.end(); it++){
			list->push_back(it->first);
		}
	}

	return *list;
}

// get constants with the given line
const Type::List::Constant& ConstantTable::getConstantsByLine(Type::Index::Line line){
	if(Utilities::isValidLineIndex(line) && isLineExistsInConstant(line)){
		return constantTable.at(line);
	}

	return *( new Type::List::Constant());
}

// get the list of lines associated with the given constant
const Type::List::Line& ConstantTable::getLinesByConstant(Type::Constant con){
	if(Utilities::isValidConstant(con) && isConstantExistsInLine(con)){
		Type::Map::EntityLine* entityLineMap = &lineTable.at(con);

		if(entityLineMap->size() > 0 && 
			entityLineMap->find( Type::Entity::ALL_LINE_ENTITY) != entityLineMap->end()){
			return entityLineMap->at( Type::Entity::ALL_LINE_ENTITY);
		}
	}

	return *(new Type::List::Line());
}

// get the list of lines associated with the given constant
const Type::List::Line& ConstantTable::getLinesByConstant(Type::Constant con, Type::Entity::Line entityType){
	if(Utilities::isValidConstant(con) && isConstantExistsInLine(con)){
		Type::Map::EntityLine* entityLineMap = &lineTable.at(con);

		if(entityLineMap->size() > 0 && entityLineMap->find(entityType) != entityLineMap->end()){
			return entityLineMap->at(entityType);
		}
	}

	return *(new Type::List::Line());
}

// check for exsistence of the given constant
bool ConstantTable::isConstantExistsInLine(Type::Constant con){
	return lineTable.size() > 0 && lineTable.find(con) != lineTable.end();
}

// check for exsistence of the given line
bool ConstantTable::isLineExistsInConstant(Type::Index::Line line){
	return constantTable.size() > 0 && constantTable.find(line) != constantTable.end();
}